package clases;

import clases.comparator.OrdenarTicketsPeajePorFechaAscendente;
import datos.GuardarDatos;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

public class Sistema {
    private final int DISTANCIA_TRAMO = 20000; // 20Km
    private final int VELOCIDAD_MAXIMIA = 120;
    private final int RADARDIJO = 1;
    private static final String FICHEROCOCHE = "coches.txt";
    private static final String FICHEROMULTA = "multas.txt";
    private static final String FICHEROTICKET = "ticket.txt";
    
    private ArrayList<CamaraInicio> listaCamarasInicio;
    private ArrayList<CamaraFin> listaCamarasFin;
    private ArrayList<Radar> listaRadares;
    private ArrayList<TicketPeaje> listaTicketPeaje;
    private ArrayList<Multa> listaMulta;
    private ArrayList<Vehiculo> listaVehiculos;
    
    private Timer tim2 = new Timer();
    private TimerTask actualizarCamaras = new TimerTask(){
        public void run() {
            comprobarCamaras();
            comprobarRadares();
        }
    };

    public Sistema() throws FileNotFoundException, IOException {
        listaCamarasInicio = new ArrayList<>();
        listaCamarasFin = new ArrayList<>();
        listaTicketPeaje = new ArrayList<>();
        listaMulta = new ArrayList<>();
        listaRadares = new ArrayList<>();
        CargarDatos();
        tim2.scheduleAtFixedRate(actualizarCamaras, 10000, 5000);

        for (int i=0;i<4;i++){
            listaCamarasFin.add(new CamaraFin(listaVehiculos));
            listaCamarasInicio.add(new CamaraInicio(listaVehiculos));
        }
    }

    public ArrayList<CamaraFin> getListaCamarasFin() {
        return listaCamarasFin;
    }

    public ArrayList<CamaraInicio> getListaCamarasInicio() {
        return listaCamarasInicio;
    }

    public ArrayList<Multa> getListaMulta() {
        return listaMulta;
    }

    public ArrayList<Radar> getListaRadares() {
        return listaRadares;
    }

    public ArrayList<TicketPeaje> getListaTicketPeaje() {
        return listaTicketPeaje;
    }

    public ArrayList<Vehiculo> getListaVehiculos() {
        return listaVehiculos;
    }
    
    public Radar crearRadar(){
        Radar r = new Radar(listaVehiculos);
        listaRadares.add(r);
        return r;
    }
    
    public void eliminarRadar(Radar r){
        listaRadares.remove(r);
    }
    
    public void comprobarCamaras(){
        for (CamaraFin c:listaCamarasFin){
            if (c.isTransmitiendo()){
                Map <Vehiculo,Date> mapa = c.getMapa();
                Iterator it = mapa.entrySet().iterator();
                while (it.hasNext()) {
                    try{
                        Map.Entry elemento = (Map.Entry)it.next();
                        Vehiculo vh = (Vehiculo) elemento.getKey();
                        Date fechaSalida = (Date) elemento.getValue();
                        Date entrada = buscarHoraEntrada(vh);
                        ComprobarVelocidadTramo(vh,fechaSalida,entrada);
                        generarTicket(vh,entrada,fechaSalida);
                        listaVehiculos.add(vh);
                        c.setTransmitiendo(false);
                        c.limpiarLista();
                    }catch(NullPointerException e){
                        System.out.println("Error. Codigo:" +e);
                    } 
                }               
            }
            c.actualizarListaVehiculos(listaVehiculos);
        }
    }
    
    public void comprobarRadares(){
        for (Radar r : listaRadares)
            if(r.isTransmitiendo()){
                listaMulta.addAll(r.transmitir());
                r.limpiarListaMultas();
            }
    }
    
    public Date buscarHoraEntrada(Vehiculo vh){
        Date horaEntrada = null;
        for (CamaraInicio c:listaCamarasInicio){
            Map <Vehiculo,Date> mapa = c.getMapa();
            Iterator it = mapa.entrySet().iterator();
            while (it.hasNext()) {
                Map.Entry elemento = (Map.Entry)it.next();
                Vehiculo vh2 = (Vehiculo) elemento.getKey();
                if (vh2.equals(vh))
                    horaEntrada = (Date) elemento.getValue();
            }
        }
        return horaEntrada;
    }
    
    public void generarTicket(Vehiculo c, Date fecha_entrada, Date fecha_salida){
        listaTicketPeaje.add(new TicketPeaje(c,fecha_entrada, fecha_salida));
    }
    
    public void agregarMulta(Multa m){
        for (Radar r:listaRadares)
            if (r.isTransmitiendo()){
                listaMulta.addAll(r.transmitir());
                r.limpiarListaMultas();
            }
    }
    
    public void ComprobarVelocidadTramo(Vehiculo c, Date fechaEntrada, Date fechaSalida){
        Calendar cal1 = Calendar.getInstance();
        Calendar cal2 = Calendar.getInstance();
        cal1.setTime(fechaSalida);
        cal2.setTime(fechaEntrada);
        
        long diferenciaSegundos = (cal2.getTimeInMillis() - cal1.getTimeInMillis()) / 1000;
        long tiempoEnTramoMaximo = (DISTANCIA_TRAMO * 3600)/(VELOCIDAD_MAXIMIA * 1000);

        if (tiempoEnTramoMaximo >= diferenciaSegundos){
            int velocidad = (int)((DISTANCIA_TRAMO / 1000) / ((double)diferenciaSegundos / 3600));
            Date d = new Date();
            listaMulta.add(new Multa(velocidad, c, RADARDIJO, d)); 
       }    
    }
    
    public String GuardarDatos() throws FileNotFoundException, IOException{
        try{
            GuardarDatos gd = new GuardarDatos();
            gd.Guardar(listaMulta, FICHEROMULTA);
            gd.Guardar(listaTicketPeaje, FICHEROTICKET);
            gd.Guardar(listaVehiculos, FICHEROCOCHE);
            return "Datos Guardados con exito";
        }catch(Exception e){
            return "Error al guardar los datos. Error: "+e;
        }
    }
    
    private void CargarDatos() throws FileNotFoundException, IOException{
        GuardarDatos gd = new GuardarDatos() {};
        listaMulta = gd.Cargar(FICHEROMULTA);
        listaTicketPeaje = gd.Cargar(FICHEROTICKET);
        listaVehiculos = gd.Cargar(FICHEROCOCHE);
    }
    
    public void cambiarEstadoMulta(Multa m, boolean valor) throws FileNotFoundException, IOException{
        for (Multa mult : listaMulta)
            if (mult.equals(m))
                m.setPagado(valor);
        GuardarDatos();
    }
    
    public ArrayList<TicketPeaje> filtradoTicketPeaje(String fecha1, String fecha2, int orden){
        ArrayList<TicketPeaje> lista = new ArrayList<>();        
        SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
        try{
            Date date1 = sdf.parse(fecha1);
            Date date2 = sdf.parse(fecha2);
            date2.setHours(23);
            date2.setMinutes(59);
            for (TicketPeaje tp : listaTicketPeaje)
                if(date1.compareTo(tp.getFecha()) < 0 && date2.compareTo(tp.getFecha()) > 0)
                    lista.add(tp);
        }catch(ParseException e){
            System.out.println("Error en el parseo de fechas: "+e);
        }
        switch (orden){
            case 0: Collections.sort(lista);
                break;
            case 1: Collections.sort(lista, new OrdenarTicketsPeajePorFechaAscendente());
                break;
        }
        return lista;        
    }
    
    public ArrayList<Multa> filtradoMultas(String fecha1, String fecha2, int orden){
        ArrayList<Multa> lista = new ArrayList<>();        
        SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
        try{
            Date date1 = sdf.parse(fecha1);
            Date date2 = sdf.parse(fecha2);
            date2.setHours(23);
            date2.setMinutes(59);
            for (Multa m : listaMulta)
                if(date1.compareTo(m.getFecha()) < 0 && date2.compareTo(m.getFecha()) > 0)
                    lista.add(m);
        }catch(ParseException e){
            System.out.println("Error en el parseo de fechas: "+e);
        }
        switch (orden){
            case 0: Collections.sort(lista);
                break;
            case 1: Collections.sort(lista, new OrdenarTicketsPeajePorFechaAscendente());
                break;
        }
        return lista;        
    }
    
    public ArrayList<Multa> filtradoMultasMatricula(String matricula){
        ArrayList<Multa> lista = new ArrayList<>();
        for (Multa m : listaMulta)
                if(matricula.equals(m.getMatricula()))
                    lista.add(m);
        Collections.sort(lista, new OrdenarTicketsPeajePorFechaAscendente());
        return lista;
    }
}
